package com.bflow.util;

import com.bflow.api.IBflowDefineActRouteService;
import com.bflow.api.IBflowDefineActService;
import com.bflow.api.IBflowDefineProcessService;
import com.bflow.api.bo.BflowDefineActBo;
import com.bflow.api.bo.BflowDefineActRouteBo;
import com.bflow.api.bo.BflowDefineProcessBo;
import com.bflow.api.constant.BflowConstant;
import com.bflow.api.constant.BflowConstant.ActAssignTypes;
import com.bflow.commons.exception.BizException;
import com.bflow.commons.util.StrUtil;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * @Name 活动路由工具类
 * @Author haowen
 * @Date 2018-12-17
 */
public class ActRouteUtil {

    private static IBflowDefineActService bflowDefineActService = SpringContextHolder.getBean("bflowDefineActService");
    private static IBflowDefineActRouteService bflowDefineActRouteService = SpringContextHolder.getBean("bflowDefineActRouteService");
    private static IBflowDefineProcessService bflowDefineProcessService = SpringContextHolder.getBean("bflowDefineProcessService");

    /**
     * 查找 下个活动路由
     * @param procId
     * @param currActId
     * @param assignType
     * @return
     */
    public static BflowDefineActRouteBo findNextActRoute(String procId, String currActId, ActAssignTypes assignType){
        List<BflowDefineActRouteBo> routeBos = bflowDefineActRouteService.findListByProcId(procId);
        BflowDefineProcessBo processBo = bflowDefineProcessService.getCache(procId);
        String[] actSet = processBo.getActArray();
        int currActdex = 0;
        for (String actId : actSet) {
            if(actId.equals(currActId)) {
                break;
            }
            currActdex++;
        }
        BflowDefineActBo actBo = null;
        for (BflowDefineActRouteBo routeBo : routeBos) {
            if(routeBo.getActSort() == currActdex && StrUtil.isNotBlank(routeBo.getNextActId())){
                actBo = bflowDefineActService.getCacheById(routeBo.getNextActId());
                if(assignType.eq(actBo.getAssigneeType())){
                    return routeBo;
                }
            }
        }
        return null;
    }

    /**
     * 是否 为一条 完整流程链接
     * @param startActId
     * @param routeBos
     * @link 最短路径: https://github.com/choudou5/algorithm-demo/blob/master/src/main/java/com/choudoufu/algorithm/graph/Bfs.java
     */
    public static boolean isFullProcessActLink(String startActId, List<BflowDefineActRouteBo> routeBos){
        Map<String, List<BflowDefineActRouteBo>> group = buildGroup(routeBos);
        //检查 活动是否重复
        checkActDuplicate(group);
        //  查询终点
        String startNode = startActId+"_0";
        //查找 邻居
        List<BflowDefineActRouteBo> neighbors = group.get(startNode);
        AssertUtil.isNotEmpty(neighbors, "流程有误, 开始节点无条件分支");
        Queue<BflowDefineActRouteBo> queue = new ArrayBlockingQueue<BflowDefineActRouteBo>(50);
        queue.addAll(neighbors);	//将邻居添加到 队列
        List<String> checked = new ArrayList();//已检查过的
        checked.add(startActId);
        boolean flag = false;
        int groupDex = 1;
        while(!queue.isEmpty()){
            BflowDefineActRouteBo routeBo = queue.poll();
            String nextActId = routeBo.getNextActId();
            if(StrUtil.isNotBlank(nextActId) && !checked.contains(nextActId)){//仅当 这个地点未被检查过
                checked.add(nextActId);
                if(StrUtil.isBlank(nextActId)){
                    System.out.println("找到终点了");
                    flag = true;
                    break;
                }else{
                    //将其邻居加入到 队列尾
                    neighbors = group.get(nextActId+"_"+(routeBo.getActSort()+1));
                    if(neighbors == null){
                        flag = true;
                    }else{
                        queue.addAll(neighbors);
                        groupDex++;
                    }
                }
            }
        }
        if(!flag && groupDex==group.size())
            flag = true;
        return flag;
    }

    //构建 分组
    public static Map<String, List<BflowDefineActRouteBo>> buildGroup(List<BflowDefineActRouteBo> routeBos){
        Map<String, List<BflowDefineActRouteBo>> map = new HashMap<>();
        String key = null;
        List<BflowDefineActRouteBo> list = null;
        for (BflowDefineActRouteBo routeBo : routeBos) {
            key = routeBo.getActId()+"_"+routeBo.getActSort();
            list = map.get(key);
            if(list == null)
                list = new ArrayList<>();
            list.add(routeBo);
            map.put(key, list);
        }
        return map;
    }

    //检查 活动是否重复
    private static void checkActDuplicate(Map<String, List<BflowDefineActRouteBo>> map){
        Set<String> actIds = new HashSet<>();
        String actId = null;
        for (String key : map.keySet()) {
            actId = key.split(BflowConstant.UNDERLINE)[0];
            AssertUtil.isTrue(!actIds.contains(actId), "活动ID:"+actId+"重复了");
            actIds.add(actId);
        }
    }

    public static Integer getActSortByActId(List<BflowDefineActRouteBo> routeBos, String actId){
        for (BflowDefineActRouteBo routeBo : routeBos) {
            if(routeBo.getActId().equals(actId)){
                return routeBo.getActSort();
            }
        }
        throw new BizException("查找路由排序号失败");
    }
}